<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-MVC.
 *
 * PHP-MVC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-MVC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-MVC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-mvc
 * @subpackage  config
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */


/**
 * ActionConfig
 *
 * @category   Melange
 * @package    php-mvc
 * @subpackage config
 * @copyright  Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @license    http://www.gnu.org/licenses/licenses.html#GPL    The GNU General Public License
 */
class ActionConfig {


    /**
     * Logger
     */
    private $log;


    // ----------------------------------------------------- Instance Variables


    /**
     * <p> The set of exception handling configurations for this action, if
     * any, keyed by the <code>type</code> property. </p>
     */
    protected $exceptions = array();


    /**
     * <p> Context-relative path of the web application resource that will
     * process this request via RequestDispatcher.forward(), instead of
     * instantiating and calling the <code>Action</code> class specified by
     * "type". Exactly one of <code>forward</code>, <code>include</code>, or
     * <code>type</code> must be specified. </p>
     */
    protected $forward = null;


    /**
     * <p> The set of local forward configurations for this action, if any,
     * keyed by the <code>name</code> property. </p>
     */
    protected $forwards = array();


    /**
     * <p> The module configuration with which we are associated. </p>
     */
    protected $moduleConfig = null;


    /**
     * <p> The request-scope or session-scope attribute name under which our
     * form bean is accessed, if it is different from the form bean's
     * specified <code>name</code>. </p>
     */
    protected $attribute = null;


    /**
     * <p>Can this Action be cancelled? [false]</p> <p> By default, when an
     * Action is cancelled, validation is bypassed and the Action should not
     * execute the business operation. If a request tries to cancel an Action
     * when cancellable is not set, a "InvalidCancelException" is thrown.
     * </p>
     */
    protected $cancellable = false;


    /**
     * <p> Context-relative path of the input form to which control should be
     * returned if a validation error is encountered.  Required if "name" is
     * specified and the input bean returns validation errors. </p>
     */
    protected $input = null;


    /**
     * <p> Name of the form bean, if any, associated with this Action. </p>
     */
    protected $name = null;


    /**
     * <p> General purpose configuration parameter that can be used to pass
     * extra information to the Action instance selected by this Action.
     * Struts does not itself use this value in any way. </p>
     */
    protected $parameter = null;


    /**
     * <p> Context-relative path of the submitted request, starting with a
     * slash ("/") character, and omitting any filename extension if extension
     * mapping is being used. </p>
     */
    protected $path = null;


    /**
     * <p> Comma-delimited list of security role names allowed to request this
     * Action. </p>
     */
    protected $roles = null;


    /**
     * <p> The set of security role names used to authorize access to this
     * Action, as an array for faster access. </p>
     */
    protected $roleNames = array();


    /**
     * <p> Identifier of the scope ("request" or "session") within which our
     * form bean is accessed, if any. </p>
     */
    protected $scope = "session";


    /**
     * <p> Fully qualified Java class name of the <code>Action</code> class to
     * be used to process requests for this mapping if the
     * <code>forward</code> and <code>include</code> properties are not set.
     * Exactly one of <code>forward</code>, <code>include</code>, or
     * <code>type</code> must be specified.
     */
    protected $type = null;


    /**
     * <p> Indicates Action be configured as the default one for this module,
     * when true.
     */
    protected $unknown = false;


    /**
     * <p> Should the <code>validate()</code> method of the form bean
     * associated with this action be called?
     */
    protected $validate = true;


    // ----------------------------------------------------------- Constructors


    public function __construct($args = array()) {

        $this->log =& LoggerManager::getLogger("ActionConfig");

        if(array_key_exists("path", $args))
            $this->path = $args["path"];

        if(array_key_exists("type", $args))
            $this->type = $args["type"];

        if(array_key_exists("validate", $args))
            $this->validate = $args["validate"];

        if(array_key_exists("input", $args))
            $this->input = $args["input"];

        if(array_key_exists("name", $args))
            $this->name = $args["name"];

        if(array_key_exists("scope", $args))
            $this->scope = $args["scope"];

        if(array_key_exists("parameter", $args))
            $this->parameter = $args["parameter"];

        if(array_key_exists("attribute", $args))
            $this->attribute = $args["attribute"];

        if(array_key_exists("forwards", $args))
            $this->forwards = $args["forwards"];

        if(array_key_exists("unknown", $args))
            $this->unknown = $args["unknown"];

        if(array_key_exists("roles", $args))
            $this->setRoles($args["roles"]);

        if(array_key_exists("cancellable", $args))
            $this->cancellable = $args["cancellable"];

        if(array_key_exists("forward", $args))
            $this->forward = $args["forward"];
    }


    // --------------------------------------------------------- Public Methods


    /**
     * <p> Return context-relative path of the submitted request, starting
     * with a slash ("/") character, and omitting any filename extension if
     * extension mapping is being used.
     */
    public function getPath() {
        return $this->path;
    }


    /**
     * <p> Set context-relative path of the submitted request, starting with a
     * slash ("/") character, and omitting any filename extension if extension
     * mapping is being used.
     *
     * @param path context-relative path of the submitted request.
     */
    public function setPath($path) {
        $this->path = $path;
    }


    public function getType() {
        return $this->type;
    }


    public function setType($type) {
        $this->type = $type;
    }


    public function getValidate() {
        return $this->validate;
    }


    public function setValidate($validate) {
        $this->validate = $validate;
    }


    /**
     * <p> Get the context-relative path of the input form to which control
     * should be returned if a validation error is encountered.
     *
     * @return context-relative path of the input form to which control should
     *         be returned if a validation error is encountered.
     */
    public function getInput() {
        return $this->input;
    }


    /**
     * <p> Set the context-relative path of the input form to which control
     * should be returned if a validation error is encountered.  Required if
     * "name" is specified and the input bean returns validation errors.
     *
     * @param input context-relative path of the input form to which control
     *              should be returned if a validation error is encountered.
     */
    public function setInput($input) {
        $this->input = $input;
    }


    /**
     * <p> Return name of the form bean, if any, associated with this Action.
     */
    public function getName() {
        return $this->name;
    }


    /**
     * @param name name of the form bean associated with this Action.
     */
    public function setName($name) {
        $this->name = $name;
    }


    /**
     * <p> Get the scope ("request" or "session") within which our form bean
     * is accessed, if any.
     */
    public function getScope() {
        return $this->scope;
    }


    /**
     * @param scope scope ("request" or "session") within which our form bean
     *              is accessed, if any.
     */
    public function setScope($scope) {
        $this->scope = $scope;
    }


    /**
     * <p> Return general purpose configuration parameter that can be used to
     * pass extra information to the Action instance selected by this Action.
     * Struts does not itself use this value in any way.
     */
    public function getParameter() {
        return $this->parameter;
    }


    /**
     * <p> General purpose configuration parameter that can be used to pass
     * extra information to the Action instance selected by this Action.
     * Struts does not itself use this value in any way.
     *
     * @param parameter General purpose configuration parameter.
     */
    public function setParameter($parameter) {
        $this->parameter = $parameter;
    }


    /**
     * <p> Returns context-relative path of the web application resource that
     * will process this request.
     *
     * @return context-relative path of the web application resource that will
     *         process this request.
     */
    public function getForward() {
        return ($this->forward);
    }


    /**
     * <p> Set the context-relative path of the web application resource that
     * will process this request. Exactly one of <code>forward</code>,
     * <code>include</code>, or <code>type</code> must be specified.
     *
     * @param forward context-relative path of the web application resource
     *                that will process this request.
     */
    public function setForward($forward) {
        $this->forward = $forward;
    }


    /**
     * <p>Find and return the <code>ForwardConfig</code> instance defining how
     * forwarding to the specified logical name should be handled. This is
     * performed by checking local and then global configurations for the
     * specified forwarding configuration. If no forwarding configuration can
     * be found, return <code>null</code>.</p>
     *
     * @param forwardName Logical name of the forwarding instance to be
     *                    returned
     * @return The local or global forward with the specified name.
     */
    public function findForward($forwardName) {

        $config = $this->findForwardConfig($forwardName);

        if (is_null($config)) {
            $config = $this->getModuleConfig()->findForwardConfig($forwardName);
        }

        if (is_null($config)) {
            if ($this->log->isDebugEnabled()) {
                $this->log->debug("Unable to find '" . $forwardName . "' forward.");
            }
        }

        return ($config);
    }


    /**
     * <p> Return the forward configuration for the specified key, if any;
     * otherwise return <code>null</code>. </p>
     *
     * @param name Name of the forward configuration to return
     */
    public function findForwardConfig($name) {
        return array_key_exists($name, $this->forwards) ? $this->forwards[$name] : null;
    }


    /**
     * <p> Return the exception configuration for the specified type, if any;
     * otherwise return <code>null</code>. </p>
     *
     * @param type Exception class name to find a configuration for
     */
    public function findExceptionConfig($type) {
        return array_key_exists($type, $this->exceptions) ? $this->exceptions[$type] : null;
    }

    /**
     * <p> Return the exception configurations for this action.  If there are
     * none, a zero-length array is returned. </p>
     */
    public function findExceptionConfigs() {
        return $this->exceptions;
    }

    /**
     * <p>Find and return the <code>ExceptionConfig</code> instance defining
     * how <code>Exceptions</code> of the specified type should be handled.
     * This is performed by checking local and then global configurations for
     * the specified exception's class, and then looking up the superclass
     * chain (again checking local and then global configurations). If no
     * handler configuration can be found, return <code>null</code>.</p>
     *
     * <p>Introduced in <code>ActionMapping</code> in Struts 1.1, but pushed
     * up to <code>ActionConfig</code> in Struts 1.2.0.</p>
     *
     * @param type Exception class for which to find a handler
     * @since Struts 1.2.0
     */
    public function findException($type) {

        // Check through the entire superclass hierarchy as needed
        $config;

        while (true) {

            // Check for a locally defined handler
            $name = is_string($type) ? $type : get_class($type);

//            echo("findException: look locally for " . $name);
            $config = $this->findExceptionConfig($name);

            if ($config !== null) {
                return ($config);
            }

            // Check for a globally defined handler
//            echo("findException: look globally for " . $name);
            $config = $this->getModuleConfig()->findExceptionConfig($name);

            if ($config !== null) {
                return ($config);
            }

            // Loop again for our superclass (if any)
            $type = get_parent_class($type);

            if ($type === null || $type === false) {
                break;
            }
        }

        return null; // No handler has been configured
    }


    /**
     * <p> The module configuration with which we are associated.
     */
    public function getModuleConfig() {
        return ($this->moduleConfig);
    }


    /**
     * <p> The module configuration with which we are associated.
     */
    public function setModuleConfig(ModuleConfig &$moduleConfig) {
        $this->moduleConfig = $moduleConfig;
    }


    /**
     * <p> Determine whether Action is configured as the default one for this
     * module. </p>
     */
    public function getUnknown() {
        return $this->unknown;
    }


    /**
     * @param unknown Indicates Action is configured as the default one for
     *                this module, when true.
     */
    public function setUnknown($unknown) {
        $this->unknown = $unknown;
    }


    public function getRoles() {
        return ($this->roles);
    }


    public function setRoles($roles) {

        $this->roles = $roles;

        if ($roles === null) {
            $this->roleNames = array();
            return;
        }

        $rolenames = explode(",", $roles);
        foreach($rolenames as $rolename)
            $this->roleNames[] = trim($rolename);
    }


    /**
     * <p> Get array of security role names used to authorize access to this
     * Action.
     */
    public function getRoleNames() {
        return ($this->roleNames);
    }


    /**
     * <p> Returns the request-scope or session-scope attribute name under
     * which our form bean is accessed, if it is different from the form
     * bean's specified <code>name</code>.
     *
     * @return attribute name under which our form bean is accessed.
     */
    public function getAttribute() {
        if (is_null($this->attribute)) {
            return ($this->name);
        } else {
            return ($this->attribute);
        }
    }


    /**
     * <p> Set the request-scope or session-scope attribute name under which
     * our form bean is accessed, if it is different from the form bean's
     * specified <code>name</code>.
     *
     * @param attribute the request-scope or session-scope attribute name
     *                  under which our form bean is access.
     */
    public function setAttribute($attribute) {
        $this->attribute = $attribute;
    }


    /**
     * <p>Create (if necessary) and return an {@link ActionForward} that
     * corresponds to the <code>input</code> property of this Action.</p>
     *
     * @return The input forward for this action mapping.
     * @since Struts 1.1
     */
    public function getInputForward() {
        return new ForwardConfig(
            array("path"=>$this->getInput())
        );
    }

    public function __toString() {
    
        $sb = "";
        $sb .= "ActionConfig[\n";
        $sb .= "\tpath=";
        $sb .= $this->path;
        $sb .= ",\n\ttype=";
        $sb .= $this->type;
        $sb .= ",\n\tvalidate=";
        $sb .= $this->validate;
        $sb .= ",\n\tinput=";
        $sb .= $this->input;
        $sb .= ",\n\tname=";
        $sb .= $this->name;
        $sb .= ",\n\tscope=";
        $sb .= $this->scope;
        $sb .= ",\n\tparameter=";
        $sb .= $this->parameter;
        $sb .= ",\n\tattribute=";
        $sb .= $this->attribute;
        $sb .= ",\n\tforwards=";
        $count = 0;
        while(list($key, $val) = each($this->forwards)) {
            $sb .= strval($val);
            if(++$count < count($this->forwards))
                $sb .= ", ";
        }
        $sb .= ",\n\tunknown=";
        $sb .= $this->unknown;
        $sb .= ",\n\troles=";
        $sb .= "(".$this->roles.")";
        $sb .= ",\n\tcancellable=";
        $sb .= $this->cancellable;
        $sb .= ",\n\tforward=";
        $sb .= $this->forward;
        $sb .= "\n]";

        return $sb;
    }


}


/**
 * <p>A JavaBean representing the configuration information of a
 * <code>&lt;controller&gt;</code> element in a Struts configuration
 * file.</p>
  */
class ControllerConfig {


    // ------------------------------------------------------------- Properties


    /**
     * <p> The content type and character encoding to be set on each response.
     * </p>
     */
    protected $contentType = "text/html";


    /**
     * <p> Should we store a Locale object in the user's session if needed?
     * </p>
     */
    protected $locale = true;


    /**
     * <p> Should we set no-cache HTTP headers on each response? </p>
     */
    protected $nocache = false;


    /**
     * <p> Should we set no-cache HTTP headers on each response? </p>
     */
    protected $forwardPattern = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($args = array()) {

        $this->log =& LoggerManager::getLogger("ActionConfig");

        if(array_key_exists("locale", $args))
            $this->locale = $args["locale"];

        if(array_key_exists("nocache", $args))
            $this->nocache = $args["nocache"];

        if(array_key_exists("forwardPattern", $args))
            $this->forwardPattern = $args["forwardPattern"];

        if(array_key_exists("contentType", $args))
            $this->input = $args["contentType"];

    }


    // --------------------------------------------------------- Public Methods


    public function getContentType() {
        return ($this->contentType);
    }


    public function setContentType($contentType) {
        $this->contentType = $contentType;
    }


    public function getLocale() {
        return ($this->locale);
    }


    public function setLocale($locale) {
        $this->locale = $locale;
    }

    public function getNocache() {
        return ($this->nocache);
    }

    public function setNocache($nocache) {
        $this->nocache = $nocache;
    }


    public function getForwardPattern() {
        return ($this->forwardPattern);
    }

    public function setForwardPattern($forwardPattern) {
        $this->forwardPattern = $forwardPattern;
    }


}



/**
 * FormBeanConfig
 *
 * @category   Melange
 * @package    php-mvc
 * @subpackage config
 * @copyright  Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @license    http://www.gnu.org/licenses/licenses.html#GPL    The GNU General Public License
 */
class FormBeanConfig {


    // ----------------------------------------------------- Instance Variables


    /**
     * The unique identifier of this form bean, which is used to reference
     * this bean in <code>ActionMapping</code> instances as well as for the
     * name of the request or session attribute under which the corresponding
     * form bean instance is created or accessed.
     */
    protected $name = null;


    /**
     * The fully qualified Java class name of the implementation class to be
     * used or generated.
     */
    protected $type = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($args) {
        if(array_key_exists("name", $args))
            $this->name = $args["name"];
        if(array_key_exists("type", $args))
            $this->type = $args["type"];
    }


    // --------------------------------------------------------- Public Methods


    public function getName() {
        return $this->name;
    }


    public function setName($name) {
        $this->name = $name;
    }


    public function getType() {
        return $this->type;
    }


    public function setType($type) {
        $this->type = $type;
    }

    public function __toString() {

        $sb = "";
        $sb .= "FormBeanConfig[";
        $sb .= "name=";
        $sb .= $this->name;
        $sb .= ", type=";
        $sb .= $this->type;
        $sb .= "]";

        return $sb;
    }

}


/**
 * ForwardConfig
 *
 * @category   Melange
 * @package    php-mvc
 * @subpackage config
 * @copyright  Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @license    http://www.gnu.org/licenses/licenses.html#GPL    The GNU General Public License
 */
class ForwardConfig {


    // ----------------------------------------------------- Instance Variables


    /**
     * The unique identifier of this forward, which is used to reference it in
     * <code>Action</code> classes.
     */
    private $name = null;


    /**
     * <p>The URL to which this <code>ForwardConfig</code> entry points, which
     * must start with a slash ("/") character.  It is interpreted according
     * to the following rules:</p>
     *
     * <ul>
     *
     * <li>If <code>contextRelative</code> property is <code>true</code>, the
     * path is considered to be context-relative within the current web
     * application (even if we are in a named module).  It will be prefixed by
     * the context path to create a server-relative URL.</li>
     *
     * <li>If the <code>contextRelative</code> property is false, the path is
     * considered to be the module-relative portion of the URL. It will be
     * used as the replacement for the <code>$P</code> marker in the
     * <code>forwardPattern</code> property defined on the {@link
     * ControllerConfig} element for our current module. For the default
     * <code>forwardPattern</code> value of <code>$C$M$P</code>, the resulting
     * server-relative URL will be the concatenation of the context path, the
     * module prefix, and the <code>path</code> from this
     * <code>ForwardConfig</code>.
     *
     * </li>
     *
     * </ul>
     */
    private $path = null;


    /**
     * Should a redirect be used to transfer control to the specified path?
     */
    private $redirect = false;


    /**
     * <p>The prefix of the module to which this <code>ForwardConfig</code>
     * entry points, which must start with a slash ("/") character.  </p>
     * <p>Usage note: If a forward config is used in a hyperlink, and a module
     * is specified, the path must lead to another action and not directly to
     * a page. This is in keeping with rule that in a modular application all
     * links must be to an action rather than a page. </p>
     */
    protected $module = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($args = array()) {

        if(array_key_exists("name", $args))
            $this->name = $args["name"];
        if(array_key_exists("path", $args))
            $this->path = $args["path"];
        if(array_key_exists("redirect", $args))
            $this->redirect = $args["redirect"];
        if(array_key_exists("module", $args))
            $this->module = $args["module"];

    }


    // --------------------------------------------------------- Public Methods


    public function getName() {
        return $this->name;
    }


    public function setName($name) {
        $this->name = $name;
    }


    public function getPath() {
        return $this->path;
    }


    public function setPath($path) {
        $this->path = $path;
    }


    public function getRedirect() {
        return $this->redirect;
    }


    public function setRedirect($redirect) {
        $this->redirect = $redirect;
    }


    public function getModule() {
        return ($this->module);
    }


    public function setModule($module) {
        $this->module = $module;
    }

    public function __toString() {
    
        $sb = "";
        $sb .= "ForwardConfig[";
        $sb .= "name=";
        $sb .= $this->name;
        $sb .= ", path=";
        $sb .= $this->path;
        $sb .= ", type=";
        $sb .= $this->type;
        $sb .= ", redirect=";
        $sb .= $this->redirect;
        $sb .= ", module=";
        $sb .= $this->module;
        $sb .= "]";

        return $sb;
    }

}


/**
 * <p>A JavaBean representing the configuration information of a
 * <code>&lt;message-resources&gt;</code> element in a Struts configuration
 * file.</p>
 *
 */
class MessageResourcesConfig {


    // ------------------------------------------------------------- Properties


    /**
     * The servlet context attributes key under which this MessageResources
     * instance is stored.
     */
    protected $key = MVC::MESSAGES_KEY;


    /**
     * Should we return <code>null</code> for unknown message keys?
     */
    protected $nullValue = true;


    /**
     * Indicates whether 'escape processing' should be performed on the error
     * message string.
     */
    private $escape = true;


    /**
     * Parameter that is passed to the <code>createResources()</code> method
     * of our MessageResourcesFactory implementation.
     */
    protected $parameter = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($args = array()) {

        if(array_key_exists("parameter", $args))
            $this->parameter = $args["parameter"];

        if(array_key_exists("nullValue", $args))
            $this->nullValue = $args["nullValue"];

        if(array_key_exists("key", $args))
            $this->key = $args["key"];

    }


    // --------------------------------------------------------- Public Methods


    public function getKey() {
        return ($this->key);
    }


    public function setKey($key) {
        $this->key = $key;
    }


    public function getNull() {
        return ($this->nullValue);
    }


    public function setNull($nullValue) {
        $this->nullValue = $nullValue;
    }


    /**
     * Indicates whether 'escape processing' should be performed on the error
     * message string.
     *
     * @since Struts 1.2.8
     */
    public function isEscape() {
        return $this->escape;
    }


    /**
     * Set whether 'escape processing' should be performed on the error
     * message string.
     *
     * @since Struts 1.2.8
     */
    public function setEscape($escape) {
        $this->escape = $escape;
    }


    public function getParameter() {
        return ($this->parameter);
    }


    public function setParameter($parameter) {
        $this->parameter = $parameter;
    }


    // --------------------------------------------------------- Public Methods


    /**
     * Return a String representation of this object.
     */
    public function __toString() {

        $sb = "MessageResourcesConfig[";
        $sb .= "factory=";
        $sb .= $this->factory;
        $sb .= ", null=";
        $sb .= $this->nullValue;
        $sb .= ", escape=";
        $sb .= $this->escape;
        $sb .= ", parameter=";
        $sb .= $this->parameter;
        $sb .= "]";

        return $sb;
    }

}


/**
 * ModuleConfig
 *
 * @category   Melange
 * @package    php-mvc
 * @subpackage config
 * @copyright  Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @license    http://www.gnu.org/licenses/licenses.html#GPL    The GNU General Public License
 */
class ModuleConfig {


    // ------------------------------------------------------------- Properties


    /**
     * <p>The prefix of the context-relative portion of the request URI, used
     * to select this configuration versus others supported by the controller
     * servlet.  A configuration with a prefix of a zero-length String is the
     * default configuration for this web module.</p>
     */
    private $prefix;


    /**
     * <p>The set of action configurations for this module, if any, keyed by
     * the <code>path</code> property.</p>
     */
    private $actionConfigs;


    /**
     * <p>The set of exception handling configurations for this module, if
     * any, keyed by the <code>type</code> property.</p>
     */
    protected $exceptions = array();


    /**
     * <p>The set of form bean configurations for this module, if any, keyed
     * by the <code>name</code> property.</p>
     */
    private $formBeans;


    /**
     * <p>The set of global forward configurations for this module, if any,
     * keyed by the <code>name</code> property.</p>
     */
    private $forwards;


    /**
     * <p>The set of message resources configurations for this module, if any,
     * keyed by the <code>key</code> property.</p>
     */
    private $messageResources;


    /**
     * <p>The controller configuration object for this module.</p>
     */
    protected $controllerConfig = null;


    /**
     * <p>The View object for this module.</p>
     */
    private $view;


    /**
     * <p>The Helper paths for this module.</p>
     */
    private $helpers;


    /**
     * <p>The View paths for this module.</p>
     */
    private $views;


    /**
     * <p>Matches action config paths against compiled wildcard patterns</p>
     */
    protected $matcher = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($prefix) {

        $this->prefix = $prefix;
        $this->actionConfigs = array();
        $this->formBeans = array();
        $this->forwards = array();
        $this->messageResources = array();
        $this->helpers = array();
        $this->views = array();

        // Todo: make view modular
//        require_once("mvc/view/View.php");
//        $this->view = new Zend_View();

    }


    // --------------------------------------------------------- Public Methods


    /**
     * <p>The controller configuration object for this module.</p>
     */
    public function getControllerConfig() {
        if (is_null($this->controllerConfig)) {
            $this->controllerConfig = new ControllerConfig();
        }
        return $this->controllerConfig;
    }


    /**
     * <p>The controller configuration object for this module.</p>
     *
     * @param cc The controller configuration object for this module.
     */
    public function setControllerConfig(ControllerConfig $cc) {
        $this->controllerConfig = $cc;
    }


    /**
     * <p>The prefix of the context-relative portion of the request URI, used
     * to select this configuration versus others supported by the controller
     * servlet.  A configuration with a prefix of a zero-length String is the
     * default configuration for this web module.</p>
     */
    public function getPrefix() {
        return ($this->prefix);
    }

    public function setPrefix($prefix) {
        $this->prefix = $prefix;
    }


    /**
     * Return the view component of this Module
     */
    public function getView() {
        return $this->view;
    }

    public function setView($view) {
        $this->view = $view;
    }


    /**
     * <p> Add a new <code>Helper</code> path to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addHelperPath($path, $prefix) {
        $this->helpers[] = array("path"=>$path, "classPrefix"=>$prefix);
    }

    public function getHelpers() {
        return ($this->helpers);
    }


    /**
     * <p> Add a new <code>View</code> path to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addViewPath($view) {
        $this->views[] = $view;
    }

    public function getViews() {
        return ($this->views);
    }


    /**
     * <p> Add a new <code>FormBeanConfig</code> instance to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addFormBeanConfig(FormBeanConfig $config) {

        $key = $config->getName();

        if (array_key_exists($key, $this->formBeans)) {
            echo("Overriding ActionForm of name " . $key);
        }

        $this->formBeans[$key] = $config;
    }


    /**
     * <p> Add a new <code>ExceptionConfig</code> instance to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addExceptionConfig(ExceptionConfig $config) {

        $key = $config->getType();
        if (array_key_exists($key, $this->exceptions)) {
            echo("Overriding ExceptionConfig of type " . $key);
        }
        $this->exceptions[$key] = $config;
        
    }


    /**
     * <p> Add a new <code>ForwardConfig</code> instance to the set of global
     * forwards associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addForwardConfig(ForwardConfig $config) {

        $key = $config->getName();

        if (array_key_exists($key, $this->forwards)) {
            echo("Overriding global ActionForward of name " . $key);
        }

        $this->forwards[$key] = $config;
    }


    /**
     * <p> Add a new <code>MessageResourcesConfig</code> instance to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addMessageResourcesConfig(MessageResourcesConfig $config) {

        $key = $config->getKey();

        if (array_key_exists($key, $this->messageResources)) {
            echo("Overriding MessageResources bundle of key " . $key);
        }

        $this->messageResources[$key] = $config;
    }


    /**
     * </p> Add a new <code>ActionConfig</code> instance to the set
     * associated with this module. </p>
     *
     * @param config The new configuration instance to be added
     * @throws IllegalStateException if this module configuration has been
     *                               frozen
     */
    public function addActionConfig(ActionConfig $config) {

        $config->setModuleConfig($this);

        $key = $config->getPath();

        if (array_key_exists($key, $this->actionConfigs)) {
            echo("Overriding ActionConfig of path " . $key . "<br />");
        }

        $this->actionConfigs[$key] = $config;
    }


    /**
     * <p> Return the form bean configuration for the specified key, if any;
     * otherwise return <code>null</code>. </p>
     *
     * @param name Name of the form bean configuration to return
     */
    public function findFormBeanConfig($name = null) {
        if(is_null($name)) {
            return $this->formBeans;
        }
        return $this->formBeans[$name];
    }


    /**
     * <p> Return the exception configuration for the specified type, if any;
     * otherwise return <code>null</code>. </p>
     *
     * @param type Exception class name to find a configuration for
     */
    public function findExceptionConfig($type=null) {

        if(is_null($type)) {
            return $this->exceptions;
        }
        return array_key_exists($type, $this->exceptions) ?
            $this->exceptions[$type] : null;
    }


    /**
     * <p>Find and return the <code>ExceptionConfig</code> instance defining
     * how <code>Exceptions</code> of the specified type should be handled.
     *
     * <p>In original Struts usage, this was only available in
     * <code>ActionConfig</code>, but there are cases when an exception could
     * be thrown before an <code>ActionConfig</code> has been identified,
     * where global exception handlers may still be pertinent.</p>
     *
     * <p>TODO: Look for a way to share this logic with
     * <code>ActionConfig</code>, although there are subtle differences, and
     * it certainly doesn't seem like it should be done with inheritance.</p>
     *
     * @param type Exception class for which to find a handler
     * @since Struts 1.3.0
     */
    public function findException($type) {

        // Check through the entire superclass hierarchy as needed
        $config = null;

        while (true) {
            // Check for a locally defined handler
            $name = $type;

            echo ("findException: look locally for " . name);
            $config = $this->findExceptionConfig($name);

            if ($config !== null) {
                return ($config);
            }

            // Loop again for our superclass (if any)
            $type = get_parent_class(type);

            echo $type;

            if ($type === null) {
                break;
            }
        }

        return (null); // No handler has been configured
    }


    /**
     * <p> Return the forward configuration for the specified key, if any;
     * otherwise return <code>null</code>. </p>
     *
     * @param name Name of the forward configuration to return
     */
    public function findForwardConfig($name = null) {
        if(is_null($name)) {
            return $this->forwards;
        }
        return array_key_exists($name, $this->forwards) ? $this->forwards[$name] : null;
    }


    /**
     * <p> Return the action configuration for the specified path, first
     * looking a direct match, then if none found, a wildcard pattern match;
     * otherwise return <code>null</code>. </p>

     * <p> If path is null Return the action configurations for this module.  If there are
     * none, a zero-length array is returned. </p>
     *
     * @param path Path of the action configuration to return
     */
    public function findActionConfig($path = null) {

        if(is_null($path)) {
            return $this->actionConfigs;
        }

        $config = array_key_exists($path, $this->actionConfigs) ?
            $this->actionConfigs[$path] : null;

        // If a direct match cannot be found, try to match action configs
        // containing wildcard patterns only if a matcher exists.
        if ((is_null($config)) && (!is_null($this->matcher))) {
            $config = $this->matcher->match($path);
        }

        return $config;
    }


    /**
     * <p> Return the message resources configuration for the specified key,
     * if any; otherwise return <code>null</code>. </p>
     *
     * @param key Key of the data source configuration to return
     */
    public function findMessageResourcesConfig($key = null) {

        if(is_null($key)) {
            return $this->messageResources;
        }

        return array_key_exists($key, $this->messageResources) ? $this->messageResources[$key] : null;
    }


    // ---------------------------------------------------------- Merge Methods


    /**
     * Merge this moduleConfig with specified moduleConfig,
     * used for multiple mapping configs on module.
     *
     * @param moduleConfig
     */
    public function merge(ModuleConfig &$moduleConfig) {

        $this->addForwardConfigs(
            $moduleConfig->findForwardConfig());

        $this->addFormBeanConfigs(
            $moduleConfig->findFormBeanConfig());

        $this->addActionConfigs(
            $moduleConfig->findActionConfig());

        $this->addMessageResourcesConfigs(
            $moduleConfig->findMessageResourcesConfig());

        $this->addExceptionConfigs(
            $moduleConfig->findExceptionConfig());

    }

    private function addForwardConfigs($configs) {
        $this->forwards += $configs;
    }

    private function addFormBeanConfigs($configs) {
        $this->formBeans += $configs;
    }

    private function addActionConfigs($configs) {
        foreach($configs as $config)
            $this->addActionConfig($config);
    }

    private function addMessageResourcesConfigs($configs) {
        $this->messageResources += $configs;
    }

    private function addExceptionConfigs($configs) {
        $this->exceptions += $configs;
    }

}


class ExceptionConfig {


    // ------------------------------------------------------------- Properties


    /**
     * The servlet context attribute under which the message resources bundle
     * to be used for this exception is located.  If not set, the default
     * message resources for the current module is assumed.
     */
    protected $bundle = null;


    /**
     * The fully qualified Java class name of the exception handler class
     * which should be instantiated to handle this exception.
     */
    protected $handler = "org.apache.struts.action.ExceptionHandler";


    /**
     * The message resources key specifying the error message associated with
     * this exception.
     */
    protected $key = null;


    /**
     * The fully qualified Java class name of the exception that is to be
     * handled by this handler.
     */
    protected $type = null;


    /**
     * The module-relative path of the resource to forward to if this
     * exception occurs during an <code>Action</code>.
     */
    protected $path = null;


    /**
     * The scope in which we should expose the ActionMessage for this
     * exception handler.
     */
    protected $scope = "request";


    // ----------------------------------------------------------- Constructors


    public function __construct($args = array()) {

        if(array_key_exists("bundle", $args))
            $this->bundle = $args["bundle"];

        if(array_key_exists("key", $args))
            $this->key = $args["key"];

        if(array_key_exists("handler", $args))
            $this->handler = $args["handler"];

        if(array_key_exists("type", $args))
            $this->type = $args["type"];

        if(array_key_exists("path", $args))
            $this->path = $args["path"];

        if(array_key_exists("scope", $args))
            $this->scope = $args["scope"];

    }


    // --------------------------------------------------------- Public Methods

    
    public function getBundle() {
        return ($this->bundle);
    }


    public function setBundle($bundle) {
        $this->bundle = $bundle;
    }


    public function isExtensionProcessed() {
        return $this->extensionProcessed;
    }


    public function getHandler() {
        return ($this->handler);
    }


    public function setHandler($handler) {
        $this->handler = $handler;
    }


    public function getKey() {
        return ($this->key);
    }


    public function setKey($key) {
        $this->key = $key;
    }


    public function getType() {
        return ($this->type);
    }


    public function setType($type) {
        $this->type = $type;
    }


    public function getPath() {
        return ($this->path);
    }


    public function setPath($path) {
        $this->path = $path;
    }


    public function getScope() {
        return ($this->scope);
    }


    public function setScope($scope) {
        $this->scope = $scope;
    }


    /**
     * Return a String representation of this object.
     */
    public function __toString() {

        $sb = "ExceptionConfig[";

        $sb .= "type=";
        $sb .= $this->type;

        if ($this->bundle !== null) {
            $sb .= ",bundle=";
            $sb .= $this->bundle;
        }

        $sb .= ",handler=";
        $sb .= this.handler;
        $sb .= ",key=";
        $sb .= this.key;
        $sb .= ",path=";
        $sb .= this.path;
        $sb .= ",scope=";
        $sb .= this.scope;
        $sb .= "]";

        return $sb;
    }
}

?>